﻿using System;
using System.Linq;
using System.Reflection;
using Extensibility;
using EnvDTE;
using EnvDTE80;
using Microsoft.VisualStudio.CommandBars;
using theeg.DependencyAnalysis.CommonAddin.Models;

namespace theeg.DependencyAnalysis.VsAddin2012
{
    /// <summary>Das Objekt für die Implementierung eines Add-Ins.</summary>
    /// <seealso class='IDTExtensibility2' />
    public class Connect : IDTExtensibility2, IDTCommandTarget
    {
        private AddinModel _addinModel;

        /// <summary>Implementiert den Konstruktor für das Add-In-Objekt. Platzieren Sie Ihren Initialisierungscode innerhalb dieser Methode.</summary>
        public Connect()
        {
            AppDomain.CurrentDomain.AssemblyResolve += CurrentDomainAssemblyResolve;
        }

        private static Assembly CurrentDomainAssemblyResolve(object sender, ResolveEventArgs args)
        {
            var domain = (AppDomain)sender;
            return domain.GetAssemblies().FirstOrDefault(assembly => assembly.FullName == args.Name);
        }
        /// <summary>Implementiert die OnConnection-Methode der IDTExtensibility2-Schnittstelle. Empfängt eine Benachrichtigung, wenn das Add-In geladen wird.</summary>
        /// <param term='application'>Stammobjekt der Hostanwendung.</param>
        /// <param term='connectMode'>Beschreibt, wie das Add-In geladen wird.</param>
        /// <param term='addInInst'>Objekt, das dieses Add-In darstellt.</param>
        /// <param name="application"></param>
        /// <param name="connectMode"></param>
        /// <param name="addInInst"></param>
        /// <param name="custom"></param>
        /// <seealso class='IDTExtensibility2' />
        public void OnConnection(object application, ext_ConnectMode connectMode, object addInInst, ref Array custom)
        {
            _applicationObject = (DTE2)application;
            _addInInstance = (AddIn)addInInst;

            _addinModel = new AddinModel(new VsSolution(_applicationObject.Solution));
            AddMenuItem(connectMode);
            OpenToolWindow();
        }

        private void AddMenuItem(ext_ConnectMode connectMode)
        {
            if (connectMode != ext_ConnectMode.ext_cm_UISetup) return;
            var contextGuids = new object[] { };
            Commands2 commands = (Commands2)_applicationObject.Commands;
            const string toolsMenuName = "Tools";

            CommandBar menuBarCommandBar = ((CommandBars)_applicationObject.CommandBars)["MenuBar"];
            CommandBarControl toolsControl = menuBarCommandBar.Controls[toolsMenuName];
            CommandBarPopup toolsPopup = (CommandBarPopup)toolsControl;

            try
            {
                Command command = commands.AddNamedCommand2(_addInInstance, "DependencyAnalysis", "Egg and Gherkin",
                                                            "Egg and Gherkin (DependencyAnalysis)", true, Type.Missing, ref contextGuids);

                if ((command != null) && (toolsPopup != null))
                {
                    command.AddControl(toolsPopup.CommandBar);
                }
            }
            catch (ArgumentException)
            {
                // TODO command already exists, ignore? 
            }
        }

        private void OpenToolWindow()
        {
            AddIn addinobj = _applicationObject.AddIns.Item(1);
            Windows2 wins2Obj = (Windows2)_applicationObject.Windows;

            string assemblypath = typeof(ToolWindow).Assembly.Location;
            string classname = typeof(ToolWindow).FullName;
            const string guidpos = "{A100728B-53D1-4142-9D9A-759E816015DB}";
            const string caption = "Egg and Gherkin (DependencyAnalysis)";

            object ctlobj = null;
            Window newWinobj = wins2Obj.CreateToolWindow2(addinobj, assemblypath,
                                                          classname, caption, guidpos, ref ctlobj);
            newWinobj.Visible = true;
            var toolWindow = (ToolWindow)newWinobj.Object;
            toolWindow.DataContext = _addinModel;
        }

        /// <summary>Implementiert die OnDisconnection-Methode der IDTExtensibility2-Schnittstelle. Empfängt eine Benachrichtigung, wenn das Add-In entladen wird.</summary>
        /// <param term='disconnectMode'>Beschreibt, wie das Add-In entladen wird.</param>
        /// <param term='custom'>Array von spezifischen Parametern für die Hostanwendung.</param>
        /// <param name="disconnectMode"></param>
        /// <param name="custom"></param>
        /// <seealso class='IDTExtensibility2' />
        public void OnDisconnection(ext_DisconnectMode disconnectMode, ref Array custom)
        {
        }

        /// <summary>Implementiert die OnAddInsUpdate-Methode der IDTExtensibility2-Schnittstelle. Empfängt eine Benachrichtigung, wenn die Auflistung von Add-Ins geändert wurde.</summary>
        /// <param term='custom'>Array von spezifischen Parametern für die Hostanwendung.</param>
        /// <param name="custom"></param>
        /// <seealso class='IDTExtensibility2' />		
        public void OnAddInsUpdate(ref Array custom)
        {
        }

        /// <summary>Implementiert die OnStartupComplete-Methode der IDTExtensibility2-Schnittstelle. Empfängt eine Benachrichtigung, wenn der Ladevorgang der Hostanwendung abgeschlossen ist.</summary>
        /// <param term='custom'>Array von spezifischen Parametern für die Hostanwendung.</param>
        /// <param name="custom"></param>
        /// <seealso class='IDTExtensibility2' />
        public void OnStartupComplete(ref Array custom)
        {
        }

        /// <summary>Implementiert die OnBeginShutdown-Methode der IDTExtensibility2-Schnittstelle. Empfängt eine Benachrichtigung, wenn die Hostanwendung entladen wird.</summary>
        /// <param term='custom'>Array von spezifischen Parametern für die Hostanwendung.</param>
        /// <param name="custom"></param>
        /// <seealso class='IDTExtensibility2' />
        public void OnBeginShutdown(ref Array custom)
        {
        }

        /// <summary>Implementiert die QueryStatus-Methode der IDTCommandTarget-Schnittstelle. Diese wird aufgerufen, wenn die Verfügbarkeit des Befehls aktualisiert wird.</summary>
        /// <param term='commandName'>Der Name des Befehls, dessen Zustand ermittelt werden soll.</param>
        /// <param term='neededText'>Für den Befehl erforderlicher Text.</param>
        /// <param term='status'>Der Zustand des Befehls in der Benutzeroberfläche.</param>
        /// <param term='commandText'>Vom neededText-Parameter angeforderter Text.</param>
        /// <param name="commandName"></param>
        /// <param name="neededText"></param>
        /// <param name="status"></param>
        /// <param name="commandText"></param>
        /// <seealso class='Exec' />
        public void QueryStatus(string commandName, vsCommandStatusTextWanted neededText, ref vsCommandStatus status, ref object commandText)
        {
            if (neededText != vsCommandStatusTextWanted.vsCommandStatusTextWantedNone) return;
            if (commandName == "DependencyAnalysis.Connect.DependencyAnalysis")
            {
                status = vsCommandStatus.vsCommandStatusSupported | vsCommandStatus.vsCommandStatusEnabled;
            }
        }

        /// <summary>Implementiert die Exec-Methode der IDTCommandTarget-Schnittstelle. Diese wird aufgerufen, wenn der Befehl aufgerufen wird.</summary>
        /// <param term='commandName'>Der Name des auszuführenden Befehls.</param>
        /// <param term='executeOption'>Beschreibt, wie der Befehl ausgeführt werden muss.</param>
        /// <param term='varIn'>Parameter, die vom Aufrufer an den Befehlshandler übergeben werden.</param>
        /// <param term='varOut'>Parameter, die vom Befehlshandler an den Aufrufer übergeben werden.</param>
        /// <param term='handled'>Informiert den Aufrufer, ob der Befehl bearbeitet wurde oder nicht.</param>
        /// <param name="commandName"></param>
        /// <param name="executeOption"></param>
        /// <param name="varIn"></param>
        /// <param name="varOut"></param>
        /// <param name="handled"></param>
        /// <seealso class='Exec' />
        public void Exec(string commandName, vsCommandExecOption executeOption, ref object varIn, ref object varOut, ref bool handled)
        {
            handled = false;
            if (executeOption != vsCommandExecOption.vsCommandExecOptionDoDefault) return;
            if (commandName == "DependencyAnalysis.Connect.DependencyAnalysis")
            {
                handled = true;
            }
        }

        private DTE2 _applicationObject;
        private AddIn _addInInstance;

        public static ToolWindow CreateToolWindow(DTE2 applicationDte)
        {
            var addinModel = new AddinModel(new VsSolution(applicationDte.Solution));
            return new ToolWindow { DataContext = addinModel };
        }
    }
}